package com.nifostasky.comic.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.nifostasky.comic.common.properties.RedisKeyProperties;
import com.nifostasky.comic.common.threadlocal.SessionLoad;
import com.nifostasky.comic.common.utils.MailUtil;
import com.nifostasky.comic.dtos.ForgetPasswordDTO;
import com.nifostasky.comic.dtos.RegisterDTO;
import com.nifostasky.comic.entitys.*;
import com.nifostasky.comic.exceptions.BusinessException;
import com.nifostasky.comic.mapper.SysUserMapper;
import com.nifostasky.comic.service.SysOrgService;
import com.nifostasky.comic.service.SysUnitService;
import com.nifostasky.comic.service.SysUserRoleService;
import com.nifostasky.comic.service.SysUserService;
import com.nifostasky.comic.utils.*;
import com.nifostasky.comic.common.utils.SysConstantUtil;
import com.nifostasky.comic.dtos.SysOrgDTO;
import com.nifostasky.comic.dtos.UserLoginDTO;
import com.nifostasky.comic.query.SysUserQuery;
import com.nifostasky.comic.vos.JsonResult;
import com.nifostasky.comic.vos.ResultCode;
import com.nifostasky.comic.vos.RoleVO;
import com.nifostasky.comic.vos.SysUserVO;
import io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
@Primary
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

    @Autowired
    private JwtUtil jwtUtil;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private SysOrgService sysOrgService;
    @Autowired
    private SysUserRoleService sysUserRoleService;
    @Autowired
    private SysUnitService sysUnitService;
    @Autowired
    @Qualifier("captchaProducerMail")
    private DefaultKaptcha captchaProducer;
    @Autowired
    private MailUtil mailUtil;

    @Override
    public IPage<SysUser> findListPage(PageUtils pageUtils, SysUserQuery  userQuery) {
        if (!SessionLoad.getLocalData().getSuperAdmin() && StringUtils.isBlank(userQuery.getUnitCode())) {
            userQuery.setUnitCode(SessionLoad.getLocalData().getUnitCode());
        }
        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.likeRight(StringUtils.isNotBlank(userQuery.getOrgCode()),SysUser::getOrgCode,userQuery.getOrgCode());
        queryWrapper.like(StringUtils.isNotBlank(userQuery.getNickName()),SysUser::getNickName,userQuery.getNickName());
        queryWrapper.like(StringUtils.isNotBlank(userQuery.getUserName()),SysUser::getUserName,userQuery.getUserName());
        queryWrapper.likeRight(StringUtils.isNotBlank(userQuery.getUnitCode()), SysUser::getUnitCode,userQuery.getUnitCode());
        queryWrapper.eq(Objects.nonNull(userQuery.getUnitId()), SysUser::getUnitId,userQuery.getUnitId());
        queryWrapper.eq(StringUtils.isNotBlank(userQuery.getPhone()),SysUser::getPhone,userQuery.getPhone());
        queryWrapper.eq(Objects.nonNull(userQuery.getUserType()),SysUser::getUserType,userQuery.getUserType());
        queryWrapper.eq(Objects.nonNull(userQuery.getState()),SysUser::getState,userQuery.getState());

        IPage<SysUser> page = this.baseMapper.selectPage(new Page<>(pageUtils.getPageIndex(),pageUtils.getPageSize()),queryWrapper);
        if(Objects.nonNull(page) && CollectionUtils.isNotEmpty(page.getRecords())){
            page.getRecords().stream().forEach(u->{
                u.setPassWord(null);
                u.setSalt(null);
                this.handleBusInfo(u);
            });
        }
        return page;
    }
    private void handleBusInfo(SysUser user){
        if(Objects.nonNull(user.getOrgId())){
            SysOrg org = sysOrgService.getById(user.getOrgId());
            if(Objects.nonNull(org)){
                user.setOrgName(org.getOrgName());
            }
        }
        if(Objects.nonNull(user.getUnitId())){
            SysUnit unit = sysUnitService.getById(user.getUnitId());
            if(Objects.nonNull(unit)){
                user.setUnitName(unit.getUnitName());
            }
        }
    }
    @Override
    public JsonResult getVerifyCode(HttpServletRequest request) {
        try {
            String key = "verify" + UUID.randomUUID().toString();
            //输出验证码图片方法
            RandomValidateCodeUtil randomValidateCode = new RandomValidateCodeUtil();
            String randCode = randomValidateCode.getRandcode(request, redisTemplate, key);
            Map<String, String> map = new HashMap<>();
            map.put("vCode", randCode);
            map.put("key", key);
            return new JsonResult(map);
        } catch (Exception e) {
            log.error("获取验证码异常:{}", e.getMessage());
            return new JsonResult(ResultCode.CHECK_CODE_EXCEPTION);
        }
    }
    public JsonResult userLogin(UserLoginDTO userLoginDTO, HttpServletRequest request) {
        //登录用户密码是否为空(提前校验是：如果未输入密码可以减少DB查询、减少IO)
        if(!org.springframework.util.StringUtils.hasLength(userLoginDTO.getPassWord())){
            return new JsonResult(ResultCode.USER_LOGIN_ERROR);
        }
        //获取用户
        SysUser hasUser = this.getOne(new QueryWrapper<SysUser>().lambda().eq(SysUser::getEmail, userLoginDTO.getEmail()));

        JsonResult checkResult = checkUser(hasUser);
        if(null != checkResult){
            return checkResult;
        }
        if(!hasUser.getPassWord().equals(new Sha256Hash(userLoginDTO.getPassWord(), hasUser.getSalt()).toHex())) {
             return new JsonResult(ResultCode.USER_LOGIN_ERROR);
        }

        SysUserVO userVO = new SysUserVO();
        //根据用户获取角色
//        findRole(userVO,hasUser);

        //返回值组装
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("userId", hasUser.getId());
        jsonObject.put("userName", hasUser.getUserName());
        jsonObject.put("email", hasUser.getEmail());
        //获取登录用户IP地址
//        jsonObject.put("IP", userLoginDTO.getIp());
//        jsonObject.put("unitCode", hasUser.getUnitCode());
//        userVO.setUnitCode(hasUser.getUnitCode());
//        jsonObject.put("orgCode", hasUser.getOrgCode());
        jsonObject.put("superAdmin",hasUser.getSuperAdmin());
//        jsonObject.put("unitId",hasUser.getUnitId());
//        userVO.setUnitId(hasUser.getUnitId());
        userVO.setSuperAdmin(hasUser.getSuperAdmin());
        userVO.setEmail(hasUser.getEmail());
//        userVO.setOrgCode(hasUser.getOrgCode());
        //根据用户和orgType获取部门信息
//        SysOrgDTO orgDTO = sysOrgService.findDetailByCode(hasUser.getOrgCode());
//        if(null != orgDTO ){
//            jsonObject.put("orgId", orgDTO.getId());
//            jsonObject.put("orgName", orgDTO.getOrgName());
//            jsonObject.put("orgType", orgDTO.getOrgType());
//            userVO.setOrgName(orgDTO.getOrgName());
//            userVO.setOrgId(orgDTO.getId());
//            userVO.setParentId(orgDTO.getParentId());
//        }
        //登录类型、IP、userAgent等信息生成token
        String userAgent = request.getHeader("user-agent");
        jsonObject.put("userAgent", userAgent);
//        jsonObject.put("loginType", userLoginDTO.getLoginType());

        String subject = jsonObject.toJSONString();
        //生成token 并存放redis中
        String jwt = null;
        try {
            jwt = jwtUtil.createJwtToken(UUID.randomUUID().toString(), hasUser.getUserName(), subject,hasUser.getId());
        } catch (Exception e) {
            log.error("创建token并存放redis中异常:{}",e);
        }
        BeanUtils.copyProperties(hasUser, userVO);
        userVO.setJwtToken(jwt);
        JsonResult jsonResult = new JsonResult();
//        userVO.setIP(userLoginDTO.getIp());
//        userVO.setUserType(hasUser.getUserType());
        userVO.setAvatar(hasUser.getAvatar());
        jsonResult.setData(userVO);
        return jsonResult;
    }

    @Override
    public JsonResult loginOut() {
        try {
            //设置用户表的lastLoginTime(退出时间)
            SysUser user = new SysUser();
            user.setLastLoginTime(DateUtil.getNowTime());
            user.setId(SessionLoad.getLocalData().getUserId());
            super.updateById(user);
            redisTemplate.delete(RedisKeysConstantUtil.TOKEN_PREFIX + SessionLoad.getLocalData().getUserId());
            return new JsonResult("退出成功");
        } catch (Exception e) {
            log.error("退出登录异常:{}", e);
            return new JsonResult(ResultCode.LOGIN_OUT_ERROR);
        }
    }

    @Override
    @Transactional
    public Boolean setRole(Long userId, String roleIds) {
        if(Objects.isNull(userId) || StringUtils.isBlank(roleIds)){
            throw  new BusinessException("缺少必填参数");
        }
        List<Long> listRoleId = Arrays.asList(roleIds.split(",")).stream()
                                .map(roleId ->Long.parseLong(roleId)).collect(Collectors.toList());
        sysUserRoleService.deleteUserRoleByUserId(userId);
        List<SysUserRole> listUserRole = new ArrayList<>();
        for(Long roleId : listRoleId){
            SysUserRole sysUserRole = new SysUserRole();
            sysUserRole.setCreateTime(new Date());
            sysUserRole.setSysRoleId(roleId);
            sysUserRole.setSysUserId(userId);
            listUserRole.add(sysUserRole);
        }
        return sysUserRoleService.saveBatch(listUserRole);
    }

    @Override
    public Boolean saveOrUpdateUser(SysUser sysUser) {
        //验证用户名
        SysUser user = this.getUserByUserName(sysUser.getUserName());
        if (Objects.nonNull(user)) {
            if (Objects.isNull(sysUser.getId())) {
                throw new BusinessException("用户名已存在");
            } else if (Objects.nonNull(sysUser.getId()) && !Objects.equals(sysUser.getId(), user.getId())) {
                throw new BusinessException("用户名已存在");
            }
        }
        if(Objects.isNull(sysUser.getId())){
            String salt = RandomStringUtils.randomAlphanumeric(20);
            sysUser.setPassWord(new Sha256Hash(sysUser.getPassWord(),salt).toHex());
            sysUser.setSalt(salt);
        }
        //根据用户所属单位获取单位信息
        SysUnit unit = sysUnitService.getById(sysUser.getUnitId());
        if(Objects.nonNull(unit)){
            SysOrg org = sysOrgService.getById(unit.getOrgId());
            if(Objects.nonNull(org)){
                sysUser.setOrgId(org.getId());
                sysUser.setOrgCode(org.getOrgCode());
            }
            sysUser.setUnitCode(unit.getUnitCode());
        }
        return this.saveOrUpdate(sysUser);
    }

    @Override
    public SysUser getUserByUserName(String userName) {
        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper();
        queryWrapper.eq(SysUser::getUserName,userName);
        return this.getOne(queryWrapper);
    }

    @Override
    public Boolean hasExistUserName(String userName) {
        if(StringUtils.isBlank(userName)){
            throw  new BusinessException("缺少必填参数");
        }
        SysUser user = this.getUserByUserName(userName);
        if(Objects.nonNull(user) && Objects.nonNull(user.getId())){
            return true;
        }
        return false;
    }

    @Override
    public List<Long> getRoleIdsByUserId(Long id) {
        List<SysRole> roles = sysUserMapper.findRoleByUserId(id);
        if(CollectionUtils.isEmpty(roles)){
            return null;
        }
        List<Long> listRoleId = roles.stream().map(role->role.getId()).collect(Collectors.toList());
        return listRoleId;
    }

    @Override
    public Boolean restPassWordById(Long id) {
        if(Objects.isNull(id)){
            throw new BusinessException(ResultCode.REQUIRED_PARAMS);
        }
        String salt = RandomStringUtils.randomAlphanumeric(20);
        String passWord =new Sha256Hash(SysConstantUtil.INIT_PASSWORD,salt).toHex();
        this.baseMapper.restPassWordById(id,salt,passWord);
        return true;
    }

    @Override
    @Transactional
    public Boolean deleteUserAllById(String ids) {
        if(StringUtils.isBlank(ids)){
            throw new BusinessException(ResultCode.REQUIRED_PARAMS);
        }
        List<String> listId = Arrays.asList(ids.split(","));
        listId.forEach(userId->{
            sysUserRoleService.deleteUserRoleByUserId(Long.parseLong(userId));
        });
        return this.removeByIds(listId);
    }

    @Override
    public Integer generateEmailCode(String email) {
        //过期时间5min
        Long exportMinute = 5L;
        String key = RedisKeyProperties.REGISTER_EMAIL_CODE + email;
        //查询键的剩余时间,如果
        if (redisTemplate.hasKey(key)){
            Long minute = redisTemplate.getExpire(key,TimeUnit.MINUTES);
            if (minute >= exportMinute - 1L){
                throw new BusinessException("获取验证码过于频繁,请一分钟后再尝试获取",-500);
            }
        }
        //生成code,
        String code = captchaProducer.createText();
        //存入redis , 时效3min
        redisTemplate.opsForValue().set(key , code, exportMinute, TimeUnit.MINUTES);
        String subject = "comic漫画|注册验证码";
        String content = String.format("[comic漫画]验证码:%s,5分钟有效,为了保障您的账户安全,请勿向他人泄露验证码信息",code);
        Boolean aBoolean = mailUtil.sendSimpleMailMessage(email, subject, content);
        if (!aBoolean){
            throw new BusinessException("验证码获取失败,请重新获取",-500);
        }
        return 1;
    }



    @Override
    public Boolean verificationEmailCode(String email, String code) {
        String key = RedisKeyProperties.REGISTER_EMAIL_CODE + email;
        if(!redisTemplate.hasKey(key))
            return false;
        String redisCode = String.valueOf(redisTemplate.opsForValue().get(key));
        if (redisCode.equals(code)) {
            //删除键,并返回true;
            redisTemplate.delete(key);
            return true;
        }
        return false;
    }

    @Override
    public Integer registerUser(RegisterDTO registerDTO) {
        if (Objects.isNull(registerDTO) || Objects.isNull(registerDTO.getEmail()) || Objects.isNull(registerDTO.getUserName())
            || Objects.isNull(registerDTO.getVerifyCode()) || Objects.isNull(registerDTO.getPassWord()))
            throw new BusinessException("缺少必填参数,请检查邮件验证码,邮箱,用户名,密码是否填写完整" , -500);
        //判断邮箱是否已经存在
        Integer count = this.count(new QueryWrapper<SysUser>().lambda()
                .eq(SysUser::getEmail, registerDTO.getEmail())
        );
        if (count > 0 ){
            throw new BusinessException("获取失败,改邮箱已存在",-500);
        }
        //验证验证码是否正确
        if (!this.verificationEmailCode(registerDTO.getEmail(), registerDTO.getVerifyCode()))
            throw new BusinessException("验证码过期或错误,请重新获取或输入正确的验证码",-500);
        SysUser sysUser = new SysUser();
        sysUser.setEmail(registerDTO.getEmail());
        sysUser.setUserName(registerDTO.getUserName());
//        sysUser.setAvatar();
        //用户权限,普通用户 false;
        sysUser.setSuperAdmin(false);
        //给密码生成盐 并 加密
        String salt = RandomStringUtils.randomAlphanumeric(20);
        sysUser.setSalt(salt);
        sysUser.setPassWord(new Sha256Hash(registerDTO.getPassWord(),salt).toHex());
        this.save(sysUser);
        return 1;
    }

    @Override
    public Integer forgetPassword(ForgetPasswordDTO forgetPasswordDTO) {
        //判断邮箱是否已经存在
        Integer count = this.count(new QueryWrapper<SysUser>().lambda()
                .eq(SysUser::getEmail,forgetPasswordDTO.getEmail())
        );
        if (count <= 0){
            throw new BusinessException("获取验证码失败,该邮箱尚未注册",-500);
        }
        if (Objects.isNull(forgetPasswordDTO) || Objects.isNull(forgetPasswordDTO.getEmail())
                || Objects.isNull(forgetPasswordDTO.getVerifyCode()) || Objects.isNull(forgetPasswordDTO.getPassword()))
            throw new BusinessException("缺少必填参数,请检查邮件验证码,邮箱,用户名,密码是否填写完整" , -500);
        //验证验证码是否正确
        if (!this.verificationEmailCode(forgetPasswordDTO.getEmail(), forgetPasswordDTO.getVerifyCode()))
            throw new BusinessException("验证码过期或错误,请重新获取或输入正确的验证码",-500);
        //修改密码
        String salt = RandomStringUtils.randomAlphanumeric(20);
        String passwordSalt = new Sha256Hash(forgetPasswordDTO.getPassword(),salt).toHex();
        this.update(new UpdateWrapper<SysUser>().lambda()
                .eq(SysUser::getEmail,forgetPasswordDTO.getEmail())
                .set(SysUser::getSalt,salt)
                .set(SysUser::getPassWord,passwordSalt)
        );
        return 1;
    }

    /**
     * 根据用户获取角色
     * @param userVO
     * @param hasUser
     */
    private void findRole(SysUserVO userVO,SysUser hasUser){
        //角色
        List<SysRole> roles = sysUserMapper.findRoleByUserId(hasUser.getId());
        List<RoleVO> roleDTOS = new ArrayList<>();
        roles.forEach(role->{
            RoleVO roleVO = new RoleVO();
            //角色数据转换,并赋值菜单数据
            BeanUtils.copyProperties(role, roleVO);
            roleDTOS.add(roleVO);
        });
        userVO.setRoles(roleDTOS);
    }

    /**
     * 检查用户是否异常;若异常则返回异常信息,否则返回null
     * @param hasUser
     * @return
     */
    private JsonResult checkUser(SysUser hasUser){
        if (null == hasUser) {
            //没有该用户
            return new JsonResult(ResultCode.USER_NOT_EXIST);
        }
        if (hasUser.getState().equals(2)) {
            //该用户已经禁用
            return new JsonResult(ResultCode.USER_ACCOUNT_FORBIDDEN);
        }
        return null;
    }
}
